<template>
  <div class="container">
    <a-card class="general-card" title="菜单管理">
      <a-form :model="searchForm" layout="inline">
        <a-form-item field="name" label="菜单名称">
          <a-input v-model="searchForm.menuName" placeholder="请输入菜单名称" />
        </a-form-item>
        <a-form-item field="name" label="状态">
          <a-select
            v-model="searchForm.status"
            placeholder="菜单状态"
            style="width: 120px"
          >
            <a-option value="0">正常</a-option>
            <a-option value="1">停用</a-option>
          </a-select>
        </a-form-item>
        <a-form-item>
          <a-space>
            <a-button type="primary">搜索</a-button>
            <a-button @click="getList()">清空</a-button>
          </a-space>
        </a-form-item>
      </a-form>
      <a-divider></a-divider>
      <div style="margin-bottom: 16px">
        <a-button type="primary" @click="modalVisible = !modalVisible">
          <template #icon>
            <icon-plus />
          </template>
          新建
        </a-button>
      </div>
      <a-table
        v-model:expandedKeys="expandedKeys"
        :columns="tableColumns"
        :data="tableData"
        :pagination="false"
        row-key="menuId"
        :loading="tableLoading"
      >
        <template #status="{ record }">
          {{ ['正常', '停用'][record.status] }}
        </template>
        <template #operate>
          <a-link status="warning" icon>
            <template #icon> <icon-edit /> </template>
            修改</a-link
          >
          <a-link icon>
            <template #icon> <icon-plus /> </template>
            新增</a-link
          >
          <a-link status="danger" icon>
            <template #icon> <icon-delete /> </template>
            删除</a-link
          >
        </template>
      </a-table>
    </a-card>
    <a-modal v-model:visible="modalVisible" :on-before-ok="handleBeforeOk">
      <template #title> 新增菜单 </template>
      <a-form ref="menuForm" :model="editForm" layout="vertical" :rules="rules">
        <a-form-item field="parentId" label="上级菜单">
          <a-tree-select
            v-model="editForm.parentId"
            :field-names="{
              key: 'menuId',
              title: 'menuName',
            }"
            :data="treeData"
            placeholder="请选择"
          ></a-tree-select>
        </a-form-item>
        <a-form-item field="menuType" label="类型选择">
          <a-radio-group v-model="editForm.menuType">
            <a-radio value="M">目录</a-radio>
            <a-radio value="C">菜单</a-radio>
            <a-radio value="F">按钮</a-radio>
          </a-radio-group>
        </a-form-item>
        <a-form-item field="icon" label="菜单图标">
          <a-input v-model="editForm.icon" placeholder="请输入arco图标名" />
        </a-form-item>
        <a-row :gutter="24">
          <a-col :span="12">
            <a-form-item
              field="menuName"
              label="菜单名称"
              validate-trigger="blur"
            >
              <a-input
                v-model="editForm.menuName"
                placeholder="请输入菜单名称"
              />
            </a-form-item>
          </a-col>
          <a-col :span="12">
            <a-form-item
              field="orderNum"
              label="显示排序"
              validate-trigger="blur"
            >
              <a-input-number
                v-model="editForm.orderNum"
                placeholder="请输入排序值"
              />
            </a-form-item>
          </a-col>
        </a-row>
        <a-row :gutter="24">
          <a-col :span="12">
            <a-form-item field="isFrame" label="是否外链">
              <template #label>
                <span>
                  <a-tooltip
                    content="选择是外链则路由地址需要以`http(s)://`开头"
                  >
                    <icon-question-circle-fill /> </a-tooltip
                  >是否外链
                </span>
              </template>
              <a-radio-group v-model="editForm.isFrame">
                <a-radio value="0">是</a-radio>
                <a-radio value="1">否</a-radio>
              </a-radio-group>
            </a-form-item>
          </a-col>
          <a-col :span="12">
            <a-form-item field="path" label="路由地址" validate-trigger="blur">
              <template #label>
                <span>
                  <a-tooltip
                    content="访问的路由地址，如：`user`，如外网地址需内链访问则以`http(s)://`开头"
                  >
                    <icon-question-circle-fill /> </a-tooltip
                  >路由地址
                </span>
              </template>
              <a-input v-model="editForm.path" placeholder="请输入路由地址" />
            </a-form-item>
          </a-col>
        </a-row>
        <a-row :gutter="24">
          <a-col :span="12">
            <a-form-item field="visible" label="显示状态">
              <template #label>
                <span>
                  <a-tooltip
                    content="选择隐藏则路由将不会出现在侧边栏，但仍然可以访问"
                  >
                    <icon-question-circle-fill /> </a-tooltip
                  >显示状态
                </span>
              </template>
              <a-radio-group v-model="editForm.visible">
                <a-radio value="0">显示</a-radio>
                <a-radio value="1">隐藏</a-radio>
              </a-radio-group>
            </a-form-item>
          </a-col>
          <a-col :span="12">
            <a-form-item field="status" label="菜单状态">
              <template #label>
                <span>
                  <a-tooltip
                    content="选择停用则路由将不会出现在侧边栏，也不能被访问"
                  >
                    <icon-question-circle-fill /> </a-tooltip
                  >菜单状态
                </span>
              </template>
              <a-radio-group v-model="editForm.status">
                <a-radio value="0">正常</a-radio>
                <a-radio value="1">停用</a-radio>
              </a-radio-group>
            </a-form-item>
          </a-col>
        </a-row>
      </a-form>
    </a-modal>
  </div>
</template>

<script lang="ts" setup>
  import { ref, h, compile, VNode } from 'vue';
  import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
  import type { FieldRule } from '@arco-design/web-vue/es/form/interface';

  import type { Menu } from '@/api/types';
  import { getMenuList, addMenu } from '@/api/system/menu';
  import { Message } from '@arco-design/web-vue';

  interface MenuTreeData {
    menuId: number;
    menuName: string;
    parentId: number;
    icon?: () => VNode;
    children?: MenuTreeData[];
  }
  const editForm = ref({
    menuName: '',
    menuType: '',
    orderNum: undefined,
    path: '',
    component: '',
    icon: '',
    isCache: '',
    isFrame: '1',
    parentId: undefined,
    perms: '',
    query: '',
    visible: '0',
    status: '0',
  });
  const modalVisible = ref(false);
  const tableLoading = ref(false);
  const expandedKeys = ref([]);
  const searchForm = ref({
    menuName: '',
    status: '0',
  });
  const tableColumns = ref<TableColumnData[]>([
    {
      title: '菜单名称',
      dataIndex: 'menuName',
      align: 'center',
    },
    {
      title: '图标',
      dataIndex: 'icon',
      align: 'center',
    },
    {
      title: '排序',
      dataIndex: 'orderNum',
      align: 'center',
    },
    {
      title: '权限标识',
      dataIndex: 'perms',
      align: 'center',
    },
    {
      title: '组件路径',
      dataIndex: 'component',
      align: 'center',
    },
    {
      title: '状态',
      dataIndex: 'status',
      slotName: 'status',
      align: 'center',
    },
    {
      title: '创建时间',
      dataIndex: 'createTime',
      align: 'center',
    },
    {
      title: '操作',
      dataIndex: '',
      slotName: 'operate',
      align: 'center',
    },
  ]);
  const tableData = ref<Menu[]>();
  const treeData = ref();
  const rules = ref<Record<string, FieldRule | FieldRule[]>>({
    parentId: [{ required: true, message: '请选择上级菜单' }],
    menuType: [{ required: true, message: '请选择类型' }],
    menuName: [{ required: true, message: '请输入菜单名称' }],
    orderNum: [{ required: true, message: '请输入排序值' }],
    path: [{ required: true, message: '请输入路由地址' }],
  });

  const menuForm = ref();

  async function handleBeforeOk() {
    if (!(await menuForm.value.validate())) {
      const res = await addMenu(editForm.value);
      if (res.code === 200) {
        Message.success('创建成功！');
        getList();
      }
      return true;
    }
    return false;
  }

  function getList() {
    tableLoading.value = true;
    getMenuList().then((res) => {
      const menuTree = makeTree(res.data as Menu[]);
      tableData.value = menuTree;
      treeData.value = getTreeSelectData(menuTree);
      tableLoading.value = false;
    });
  }

  function makeTree(treeNodes: Menu[]): Menu[] {
    // 提前生成节点查找表。
    // 如果明确节点是顺序可以保证先父后子，可以省去这次遍历，在后面边遍历过程中填充查找表
    const nodesMap = new Map<number, Menu>(
      treeNodes.map((node) => [node.menuId, node])
    );
    // 引入虚拟根节点来统一实现 parent 始终有效，避免空判断
    const virtualRoot = {} as Partial<Menu>;
    treeNodes.forEach((node, i) => {
      if (!node.children?.length) {
        node.children = undefined;
      }
      const parent = nodesMap.get(node.parentId) ?? virtualRoot;
      (parent.children ??= []).push(node);
    });
    return virtualRoot.children ?? [];
  }

  function getTreeSelectData(list: Menu[]) {
    // 组装根节点
    const root: MenuTreeData[] = [
      {
        menuId: 0,
        menuName: '根节点',
        parentId: -1,
        children: [],
      },
    ];
    // 菜单树数据转化
    function handleData(list: Menu[]): MenuTreeData[] {
      const handleMap: MenuTreeData[] = list.map<MenuTreeData>((item: Menu) => {
        const temp: MenuTreeData = {
          menuId: item.menuId,
          menuName: item.menuName,
          parentId: item.parentId,
          icon: item.icon ? () => h(compile(`<${item.icon}/>`)) : undefined,
        };
        // 处理children
        if (item.children?.length) {
          const tempChildren = handleData(item.children);
          temp.children = tempChildren;
        }
        return temp;
      });
      return handleMap;
    }
    if (list.length) {
      root[0].children = handleData(list);
    }
    return root;
  }

  getList();
</script>

<script lang="ts">
  export default {
    name: 'MenuList',
  };
</script>

<style scoped lang="less">
  .container {
    padding: 20px;
  }

  :deep(.arco-table-th) {
    &:last-child {
      .arco-table-th-item-title {
        margin-left: 16px;
      }
    }
  }

  .action-icon {
    margin-left: 12px;
    cursor: pointer;
  }

  .active {
    color: #0960bd;
    background-color: #e3f4fc;
  }

  .setting {
    display: flex;
    align-items: center;
    width: 200px;

    .title {
      margin-left: 12px;
      cursor: pointer;
    }
  }
</style>
